/*
 * queues.cpp
 *
 *  Created on: Dec 9, 2012
 *      Author: omnia
 */
#include             "global.h"
#include             "syscalls.h"
#include             "protos.h"
#include             "string.h"
#include            "Definitions.h"

void add_to_eventQueue ( INT32 *device_id, INT32 *status ){
	//Set space for the new event PCB
	EVENT_t *event = (EVENT_t *)(malloc(sizeof(EVENT_t)));
	event->id = inc_event;
	event->device_ID = *device_id;
	event->Status = *status;
	event->next = NULL;

	// Increase global event_count
	event_count++;
	inc_event++;
	// Different typedef, could not use default add_to_Queue
	EVENT_t * ptrCheck = eventList;
	if (ptrCheck == NULL){
		eventList = event;
		return;
	}
	while(ptrCheck->next != NULL){
		ptrCheck = (EVENT_t*)ptrCheck->next;
	}
	ptrCheck->next = event;
}

void rm_from_eventQueue(INT32 remove_id) {
	EVENT_t *ptrDel = eventList;
	EVENT_t *ptrPrev = NULL;

	while (ptrDel != NULL) {
		if (ptrDel->id == remove_id) {
			event_count--;
			//First ID
			if (ptrPrev == NULL) {
				eventList = (EVENT_t*) ptrDel->next;
				return;
			}
			//Last ID
			else if (ptrDel->next == NULL) {
				ptrPrev->next = NULL;
				return;
			}
			//Midle ID
			else {
				ptrPrev->next = (EVENT_t*) ptrDel->next;
				return;
			}
		}
		ptrPrev = ptrDel;
		ptrDel = (EVENT_t*) ptrDel->next;
	}
}

void readyQueue_to_timerQueue(INT32 remove_id) {
	PCB *temp;

	ZCALL( lockReady());
	CALL( temp = ready_to_Wait(remove_id));
	ZCALL( unlockReady());

	if (temp != NULL) {
		CALL( add_to_timerQueue(&timerList, temp));
	}
}

void add_to_readyQueue(PCB **ptrFirst, PCB *entry) {
	//printf("--------------------here");
	total_pid++;
	entry->state = READY_STATE;
	//lock the ready queue
	INT32 LockResult;
	ZCALL(Z502_READ_MODIFY(MEMORY_INTERLOCK_BASE + 1, 1, true, &LockResult));
	//--------
	CALL(add_to_Queue(ptrFirst,entry));
	//sort the queue
	CALL(ready_sort());
//--------
	//unlock
	ZCALL(Z502_READ_MODIFY(MEMORY_INTERLOCK_BASE + 1, 0, true, &LockResult));
}
void add_to_timerQueue(PCB **ptrFirst, PCB *entry) {
	PCB* newPCB = (PCB*) (malloc(sizeof(PCB)));
	memcpy(newPCB, entry, sizeof(PCB));
	newPCB->next = NULL;
	newPCB->prev = NULL;
	//lock timer
	INT32 LockResult;
	ZCALL(Z502_READ_MODIFY(MEMORY_INTERLOCK_BASE, 1, true, &LockResult));
	CALL(add_to_Queue(ptrFirst, newPCB));
	CALL(timer_sort());
	//unlock
	ZCALL(Z502_READ_MODIFY(MEMORY_INTERLOCK_BASE, 0, true, &LockResult));
}
//helper function for adding in Queue
INT32 add_to_Queue(PCB **ptrFirst, PCB * entry) {
	if (*ptrFirst == NULL) {
		//first item
		(*ptrFirst) = entry;
		return 1;
	} else {
		(*ptrFirst)->prev = entry;
		entry->next = (*ptrFirst);
		(*ptrFirst) = entry;
		return 1;
	}
	return 0;
}

PCB *rm_from_Queue(PCB **ptrFirst, INT32 remove_id) {
	PCB* ptrDelete = *ptrFirst;
	PCB* ptrPrev = NULL;
	while (ptrDelete != NULL) {
		if (ptrDelete->processId == remove_id) {
			if (ptrPrev == NULL) {
//first element .. remove the head
				(*ptrFirst) = (PCB*) ptrDelete->next;
				return ptrDelete;
			}
			if (ptrDelete->next == NULL) {
				//the tail of the queue
				ptrPrev->next = NULL;
				return ptrDelete;
			}
			//normal case
			PCB *nextPtr = (PCB*) ptrDelete->next;
			ptrPrev->next = nextPtr;
			nextPtr->prev = ptrPrev;
			return ptrDelete;
		}
		ptrPrev = ptrDelete;
		ptrDelete = (PCB*) ptrDelete->next;
	}

	return NULL;
}
void rm_from_timerQueue(PCB **ptrFirst, INT32 remove_id) {
	//lock timer
	INT32 LockResult;
	ZCALL(Z502_READ_MODIFY(MEMORY_INTERLOCK_BASE, 1, true, &LockResult));
	rm_from_Queue(ptrFirst, remove_id);
	//unlock
	ZCALL(Z502_READ_MODIFY(MEMORY_INTERLOCK_BASE, 0, true, &LockResult));
}

INT32 rm_from_readyQueue(INT32 remove_id) {
	PCB* temp;
	total_pid--;
//lock ready
	lockReady();
	//must be called with CALL
	temp = rm_from_Queue(&pidList, remove_id);
	if (temp == NULL && (total_pid == 0)) {
		total_pid++;
		unlockReady();
		return -1;
	} else {
		unlockReady();
		return 1;

	}
}
void wake_timerList(INT32 currentTime, INT32* number) {
	ZCALL(
			INT32 LockResult; Z502_READ_MODIFY( MEMORY_INTERLOCK_BASE, 1, true, &LockResult ));
	PCB *ptrCheck = timerList;
	PCB *ptrPrev = NULL;
	INT32 count = 0;

//	while (ptrCheck != NULL){
//		ptrPrev = ptrCheck;
//		ptrCheck = (PCB*)ptrCheck->next;
//	}
	ptrCheck = ptrPrev;
	while (ptrCheck != NULL) {
		if (ptrCheck->time <= currentTime) {
			CALL( timerQueue_to_readyQueue(ptrCheck->processId));
			count++;

		}
		ptrCheck = (PCB*) ptrCheck->next;
	}
	ZCALL(
			INT32 LockResult; Z502_READ_MODIFY( MEMORY_INTERLOCK_BASE, 0, true, &LockResult ););
	*number = count;
}

void wait_to_Ready(INT32 remove_id) {
	PCB * ptrDel = pidList;

	while (ptrDel != NULL) {
		if (ptrDel->processId == remove_id) {
			ptrDel->state = READY_STATE;
			return;
		}
		ptrDel = (PCB*) ptrDel->next;
	}
	//No ID in PCB List
	return;
}

void timerQueue_to_readyQueue(INT32 remove_id) {
	PCB *temp;

	CALL( temp = rm_from_Queue(&timerList, remove_id));

	ZCALL( lockReady());
	CALL( wait_to_Ready(remove_id));
	ZCALL( unlockReady());
}

PCB* get_readyPCB(void) {
	lockReady();
	PCB* currPtr = pidList;
	PCB* retPTR = NULL;
	while (currPtr != NULL) {
		if (currPtr->state == READY_STATE) {
			if ((retPTR == NULL) || (retPTR->pri >= currPtr->pri))
				//first check in order not to return null if it's the head
				//second check to find the min
				retPTR = currPtr;

		}
		currPtr = (PCB*) currPtr->next;
	}
	unlockReady();
	return retPTR;
}
INT32 check_pid_ID(INT32 check_ID) {
	PCB* currPTR = pidList;
	while (currPTR != NULL) {
		if (currPTR->processId == check_ID) {
			return 0;
		}
		currPTR = (PCB*) currPTR->next;
	}
	//doesn't exist
	return -1;
}
INT32 add_to_Outbox(MSG *entry) {
	(current_PCB->msg_count)++;
	if (current_PCB->msg_count > MAX_MSG_COUNT) {
		return -1;
	}
	MSG *toSend = (MSG*)current_PCB->Outbox;

	if (toSend == NULL) {
		//it's the first msg
		current_PCB->Outbox = entry;
		return 1;
	}
	while (toSend->next != NULL) {
		//else insert at the chain
		toSend = (MSG*)toSend->next;
	}

	toSend->next = entry;
	return 1;

}
void add_to_Inbox(PCB *dest, MSG *msgRecv) {
	if (msgRecv == NULL)
		return;
	//create the new msg .. copy the data
	MSG *myMSG = (MSG *) (malloc(sizeof(MSG)));
	myMSG->dest_ID = msgRecv->dest_ID;
	myMSG->src_ID = msgRecv->src_ID;
	myMSG->Length = msgRecv->Length;
	memset(myMSG->message, 0, MAX_MSG + 1);
	strcpy(myMSG->message, msgRecv->message);
	myMSG->next = NULL;

	MSG *Inbox = (MSG*)dest->Inbox;

	//If First Message
	if (Inbox == NULL) {
		dest->Inbox = myMSG;
		return;
	}
	//else insert at the end of the chain
	while (Inbox->next != NULL) {
		Inbox =(MSG*) Inbox->next;
	}
	Inbox->next = myMSG;
}
